#include <glib.h>
#include <SDL.h>
#include "racanhack.h"
#include "cave.h"

enum {
  CAVE_LX = 2,
  CAVE_LY = 2,
  CAVE_HX = MAP_W - 2,
  CAVE_HY = MAP_H - 2,
  MINIMUM_ROOM_SIZE = 4,
  MARGIN_BETWEEN_RECT_ROOM = 2,
  MINIMUM_RECT_SIZE = MINIMUM_ROOM_SIZE + (MARGIN_BETWEEN_RECT_ROOM * 2),
  COUPLE_VERTICAL = 0,
  COUPLE_HORIZONAL = 1
};

static struct _cave *cave;
static void line_map_pass(int x0, int y0, int x1, int y1);
static void line_waynavi(int x0, int y0, int x1, int y1);
static void cave_rect_split(struct _rect *rect_parent);
static void cave_couple_more();
static void cave_room_make();
static void cave_couple_make();
static void cave_map_set();
static void cave_map_print_debug();
static struct _rect *cave_rect_add(int lx, int ly, int hx, int hy);
static struct _couple *cave_couple_add(int v_or_h, struct _rect *rect0, struct _rect *rect1);
static struct _room *cave_room_add(int lx, int ly, int hx, int hy, gboolean route);

static void line_map_pass(int x0, int y0, int x1, int y1)
{
  int min_x, max_x, min_y, max_y, i, j;
  min_x = MIN(x0, x1);
  max_x = MAX(x0, x1);
  min_y = MIN(y0, y1);
  max_y = MAX(y0, y1);
  g_assert((min_x >= 0) && (max_x < MAP_W) && (min_y >= 0) && (max_y < MAP_H));
  if ((x0 <= x1) && (y0 >= y1)) {
    for (i = min_x; i <= max_x; i++) cave->map[i][max_y].pass = TRUE;
    for (j = min_y; j <= max_y; j++) cave->map[max_x][j].pass = TRUE;
    return;
  };
  if ((x0 > x1) && (y0 > y1)) {
    for (i = min_x; i <= max_x; i++) cave->map[i][min_y].pass = TRUE;
    for (j = min_y; j <= max_y; j++) cave->map[max_x][j].pass = TRUE;
    return;
  };
  if ((x0 > x1) && (y0 <= y1)) {
    for (i = min_x; i <= max_x; i++) cave->map[i][min_y].pass = TRUE;
    for (j = min_y; j <= max_y; j++) cave->map[min_x][j].pass = TRUE;
    return;
  };
  if ((x0 <= x1) && (y0 < y1)) {
    for (i = min_x; i <= max_x; i++) cave->map[i][max_y].pass = TRUE;
    for (j = min_y; j <= max_y; j++) cave->map[min_x][j].pass = TRUE;
    return;
  };
}

static void line_waynavi(int x0, int y0, int x1, int y1)
{
  int min_x, max_x, min_y, max_y, i, j;
  min_x = MIN(x0, x1);
  max_x = MAX(x0, x1);
  min_y = MIN(y0, y1);
  max_y = MAX(y0, y1);
  g_assert((min_x >= 0) && (max_x < MAP_W) && (min_y >= 0) && (max_y < MAP_H));
  if ((x0 <= x1) && (y0 >= y1)) {
    for (i = min_x; i <= max_x; i++) cave->map[i][max_y].waynavi = TRUE;
    for (j = min_y; j <= max_y; j++) cave->map[max_x][j].waynavi = TRUE;
    return;
  };
  if ((x0 > x1) && (y0 > y1)) {
    for (i = min_x; i <= max_x; i++) cave->map[i][min_y].waynavi = TRUE;
    for (j = min_y; j <= max_y; j++) cave->map[max_x][j].waynavi = TRUE;
    return;
  };
  if ((x0 > x1) && (y0 <= y1)) {
    for (i = min_x; i <= max_x; i++) cave->map[i][min_y].waynavi = TRUE;
    for (j = min_y; j <= max_y; j++) cave->map[min_x][j].waynavi = TRUE;
    return;
  };
  if ((x0 <= x1) && (y0 < y1)) {
    for (i = min_x; i <= max_x; i++) cave->map[i][max_y].waynavi = TRUE;
    for (j = min_y; j <= max_y; j++) cave->map[min_x][j].waynavi = TRUE;
    return;
  };
}

void cave_generate_tutorial(struct _cave *c)
{
  int i, j;
  struct _room *room;
  cave = c;
  for (i = 0; i < MAP_W; i++) {
    for (j = 0; j < MAP_H; j++) {
      cave->map[i][j].pass = FALSE;
      cave->map[i][j].waynavi = FALSE;
      cave->map[i][j].passed = FALSE;
      cave->map[i][j].disturb = FALSE;
      cave->map[i][j].room = NULL;
    };
  };
  cave->rect_list = NULL;
  cave->couple_list = NULL;
  cave->room_list = NULL;
  room = cave_room_add(CAVE_LX, CAVE_LY, 10, 10, FALSE);
  for (i = room->lx; i <= room->hx; i++) {
    for (j = room->ly; j <= room->hy; j++) {
      cave->map[i][j].pass = TRUE;
      cave->map[i][j].room = room;
    };
  };
}

void cave_free(struct _cave *c)
{
  g_list_foreach(c->rect_list, (GFunc)my_free, NULL);
  g_list_free(c->rect_list);
  g_list_foreach(c->room_list, (GFunc)my_free, NULL);
  g_list_free(c->room_list);
  g_list_foreach(c->couple_list, (GFunc)my_free, NULL);
  g_list_free(c->couple_list);
}

void cave_generate(struct _cave *c)
{
  int i, j;
  GList *li, *next;
  struct _room *room;
  cave = c;
  for (j = 0; j < MAP_H; j++) {
    for (i = 0; i < MAP_W; i++) {
      cave->map[i][j].pass = FALSE;
      cave->map[i][j].waynavi = FALSE;
      cave->map[i][j].passed = FALSE;
      cave->map[i][j].disturb = FALSE;
      cave->map[i][j].room = NULL;
    };
  };
  cave->rect_list = NULL;
  cave->couple_list = NULL;
  cave->room_list = NULL;
  cave_rect_split(cave_rect_add(CAVE_LX, CAVE_LY, CAVE_HX, CAVE_HY));
  cave_couple_more();
  cave_room_make();
  cave_couple_make();
  cave_map_set();
  cave_map_print_debug();
  for (li = g_list_first(cave->room_list); li != NULL; li = next) {
    next = g_list_next(li);
    room = (struct _room *)li->data;
    if (room->route == TRUE) {
      cave->room_list = g_list_remove(cave->room_list, room);
      my_free(room);
    };
  };
}

static void cave_rect_split(struct _rect *rect_parent)
{

  struct _rect *rect_child;
  if (rect_parent->hy - rect_parent->ly <= MINIMUM_RECT_SIZE * 2) {
    rect_parent->done_split_v = TRUE;
  };
  if (rect_parent->hx - rect_parent->lx <= MINIMUM_RECT_SIZE * 2) {
    rect_parent->done_split_h = TRUE;
  };
  if ((rect_parent->done_split_v) &&
      (rect_parent->done_split_h)) {
    return;
  };
  if (g_random_int_range(0, 64) == 0) return;
  rect_child = cave_rect_add(rect_parent->lx, rect_parent->ly,
			     rect_parent->hx, rect_parent->hy);
  if (rect_parent->done_split_v == FALSE) {
    int split_coord_y;
    split_coord_y = g_random_int_range(rect_parent->ly + MINIMUM_RECT_SIZE, rect_parent->hy - MINIMUM_RECT_SIZE);
    rect_parent->hy = split_coord_y;
    rect_child->ly = split_coord_y;
    rect_parent->done_split_v = TRUE;
    rect_child->done_split_v = TRUE;
    cave_couple_add(COUPLE_VERTICAL, rect_parent, rect_child);
    cave_rect_split(rect_parent);
    cave_rect_split(rect_child);
    return;
  };
  if (rect_parent->done_split_h == FALSE) {
    int split_coord_x;
    split_coord_x = g_random_int_range(rect_parent->lx + MINIMUM_RECT_SIZE, rect_parent->hx - MINIMUM_RECT_SIZE);
    rect_parent->hx = split_coord_x;
    rect_child->lx = split_coord_x;
    rect_parent->done_split_h = TRUE;
    rect_child->done_split_h = TRUE;
    cave_couple_add(COUPLE_HORIZONAL, rect_parent, rect_child);
    cave_rect_split(rect_parent);
    cave_rect_split(rect_child);
    return;
  };
}

static void cave_couple_more()
{
  GList *li;
  struct _rect *rect;
  struct _rect *rectmap[MAP_W][MAP_H];
  int i, j;
  for (li = g_list_first(cave->rect_list); li != NULL; li = g_list_next(li)) {
    rect = (struct _rect *)li->data;
    for (i = rect->lx; i < rect->hx; i++) {
      for (j = rect->ly; j < rect->hy; j++) {
      	rectmap[i][j] = rect;
      };
    };
  };
  for (i = CAVE_LX; i < CAVE_HX - 1; i++) {
    for (j = CAVE_LY; j < CAVE_HY - 1; j++) {
      g_assert(rectmap[i][j] != NULL);
      g_assert(rectmap[i + 1][j] != NULL);
      g_assert(rectmap[i][j + 1] != NULL);
      if (rectmap[i][j] != rectmap[i][j + 1]) {
	if (g_random_int_range(0, 64) == 0) {
	  cave_couple_add(COUPLE_VERTICAL, rectmap[i][j], rectmap[i][j + 1]);
	};
      };
      if (rectmap[i][j] != rectmap[i + 1][j]) {
	if (g_random_int_range(0, 64) == 0) {
	  cave_couple_add(COUPLE_HORIZONAL, rectmap[i][j], rectmap[i + 1][j]);
	};
      };
    };
  };
}

static void cave_room_make()
{

  GList *li;
  struct _rect *rect;
  int x, y, w, h;
  gboolean route;
  for (li = g_list_first(cave->rect_list); li != NULL; li = g_list_next(li)) {
    rect = (struct _rect *)li->data;
    w = g_random_int_range(MINIMUM_ROOM_SIZE, rect->hx - rect->lx - (MARGIN_BETWEEN_RECT_ROOM * 2) + 1);
    h = g_random_int_range(MINIMUM_ROOM_SIZE, rect->hy - rect->ly - (MARGIN_BETWEEN_RECT_ROOM * 2) + 1);
    x = g_random_int_range(rect->lx + MARGIN_BETWEEN_RECT_ROOM, rect->hx - MARGIN_BETWEEN_RECT_ROOM - w + 1);
    y = g_random_int_range(rect->ly + MARGIN_BETWEEN_RECT_ROOM, rect->hy - MARGIN_BETWEEN_RECT_ROOM - h + 1);
    route = FALSE;
    if (g_random_int_range(0, 32) == 0) {
      if (g_list_length(cave->rect_list) > 1) {
	route = TRUE;
      };
    };
    rect->room = cave_room_add(x, y, x + w, y + h, route);
  };
}

static void cave_couple_make()
{
  GList *li;
  struct _couple *couple;
  for (li = g_list_first(cave->couple_list); li != NULL; li = g_list_next(li)) {
    couple = (struct _couple *)li->data;
    switch (couple->v_or_h) {
    case COUPLE_HORIZONAL:
      couple->couner0.x = couple->rect0->hx;
      couple->couner0.y = g_random_int_range(couple->rect0->room->ly + 1, couple->rect0->room->hy);
      couple->couner1.x = couple->rect1->lx;
      couple->couner1.y = g_random_int_range(couple->rect1->room->ly + 1, couple->rect1->room->hy);
      break;
    case COUPLE_VERTICAL:
      couple->couner0.x = g_random_int_range(couple->rect0->room->lx + 1, couple->rect0->room->hx);
      couple->couner0.y = couple->rect0->hy;
      couple->couner1.x = g_random_int_range(couple->rect1->room->lx + 1, couple->rect1->room->hx);
      couple->couner1.y = couple->rect1->ly;
    };
  };
}

static void cave_map_set()
{
  GList *li;
  struct _room *room;
  int i, j;
  struct _couple *couple;
  for (li = g_list_first(cave->room_list); li != NULL; li = g_list_next(li)) {
    room = (struct _room *)li->data;
    if (room->route == TRUE) continue;
    for (i = room->lx; i <= room->hx; i++) {
      for (j = room->ly; j <= room->hy; j++) {
      	cave->map[i][j].pass = TRUE;
      	cave->map[i][j].room = room;
      };
    };
  };
  for (li = g_list_first(cave->couple_list); li != NULL; li = g_list_next(li)) {
    couple = (struct _couple *)li->data;
    switch (couple->v_or_h) {
    case COUPLE_HORIZONAL:
      g_assert(couple->rect0->hx == couple->rect1->lx);
      cave->map[couple->couner0.x][couple->couner0.y].disturb = TRUE;
      cave->map[couple->couner1.x][couple->couner1.y].disturb = TRUE;
      cave->map[couple->rect0->room->hx][couple->couner0.y].disturb = TRUE;
      cave->map[couple->rect0->room->hx + 1][couple->couner0.y].disturb = TRUE;
      cave->map[couple->rect1->room->lx][couple->couner1.y].disturb = TRUE;
      cave->map[couple->rect1->room->lx - 1][couple->couner1.y].disturb = TRUE;
      line_map_pass(couple->couner0.x, couple->couner0.y,
 		   couple->couner1.x, couple->couner1.y);
      line_map_pass(couple->rect0->room->hx, couple->couner0.y,
		   couple->couner0.x, couple->couner0.y);
      line_map_pass(couple->rect1->room->lx, couple->couner1.y,
		   couple->couner1.x, couple->couner1.y);
      if (couple->rect0->room->route == TRUE) {
	line_map_pass(couple->rect0->room->hx, couple->couner0.y,
		     (couple->rect0->room->lx + couple->rect0->room->hx) / 2,
		     (couple->rect0->room->ly + couple->rect0->room->hy) / 2);
      };
      if (couple->rect1->room->route == TRUE) {
	line_map_pass(couple->rect1->room->lx, couple->couner1.y,
		     (couple->rect1->room->lx + couple->rect1->room->hx) / 2,
		     (couple->rect1->room->ly + couple->rect1->room->hy) / 2);
      };
      line_waynavi(couple->couner0.x, couple->couner0.y,
			couple->couner1.x, couple->couner1.y);
      line_waynavi(couple->rect0->room->hx, couple->couner0.y,
			couple->couner0.x, couple->couner0.y);
      line_waynavi(couple->rect1->room->lx, couple->couner1.y,
			couple->couner1.x, couple->couner1.y);
      line_waynavi(couple->rect0->room->hx, couple->couner0.y,
			(couple->rect0->room->lx + couple->rect0->room->hx) / 2,
			(couple->rect0->room->ly + couple->rect0->room->hy) / 2);
      line_waynavi(couple->rect1->room->lx, couple->couner1.y,
			(couple->rect1->room->lx + couple->rect1->room->hx) / 2,
			(couple->rect1->room->ly + couple->rect1->room->hy) / 2);
      break;
    case COUPLE_VERTICAL:
      g_assert(couple->rect0->hy == couple->rect1->ly);
      cave->map[couple->couner0.x][couple->couner0.y].disturb = TRUE;
      cave->map[couple->couner1.x][couple->couner1.y].disturb = TRUE;
      cave->map[couple->couner0.x][couple->rect0->room->hy].disturb = TRUE;
      cave->map[couple->couner0.x][couple->rect0->room->hy + 1].disturb = TRUE;
      cave->map[couple->couner1.x][couple->rect1->room->ly].disturb = TRUE;
      cave->map[couple->couner1.x][couple->rect1->room->ly - 1].disturb = TRUE;
      line_map_pass(couple->couner0.x, couple->couner0.y,
		   couple->couner1.x, couple->couner1.y);
      line_map_pass(couple->couner0.x, couple->rect0->room->hy,
		   couple->couner0.x, couple->couner0.y);
      line_map_pass(couple->couner1.x, couple->rect1->room->ly,
		   couple->couner1.x, couple->couner1.y);
      if (couple->rect0->room->route == TRUE) {
	line_map_pass(couple->couner0.x, couple->rect0->room->hy,
		      (couple->rect0->room->lx + couple->rect0->room->hx) / 2,
		      (couple->rect0->room->ly + couple->rect0->room->hy) / 2);
      };
      if (couple->rect1->room->route == TRUE) {
	line_map_pass(couple->couner1.x, couple->rect1->room->ly,
		     (couple->rect1->room->lx + couple->rect1->room->hx) / 2,
		     (couple->rect1->room->ly + couple->rect1->room->hy) / 2);
      };
      line_waynavi(couple->couner0.x, couple->couner0.y,
			couple->couner1.x, couple->couner1.y);
      line_waynavi(couple->couner0.x, couple->rect0->room->hy,
			couple->couner0.x, couple->couner0.y);
      line_waynavi(couple->couner1.x, couple->rect1->room->ly,
			couple->couner1.x, couple->couner1.y);
      line_waynavi(couple->couner0.x, couple->rect0->room->hy,
			(couple->rect0->room->lx + couple->rect0->room->hx) / 2,
			(couple->rect0->room->ly + couple->rect0->room->hy) / 2);
      line_waynavi(couple->couner1.x, couple->rect1->room->ly,
			(couple->rect1->room->lx + couple->rect1->room->hx) / 2,
			(couple->rect1->room->ly + couple->rect1->room->hy) / 2);
      break;
    };
  };
}

static void cave_map_print_debug()
{
  int i, j;
  int debug_map[MAP_W][MAP_H];
  GList *li;
  struct _rect *rect;
  return;
  for (i = 0; i < MAP_W; i++) {
    for (j = 0; j < MAP_H; j++) {
      debug_map[i][j] = 0;
    };
  };
  for (li = g_list_first(cave->rect_list); li != NULL; li = g_list_next(li)) {
    rect = (struct _rect *)li->data;
    for (i = rect->lx, j = rect->ly; i <= rect->hx; i++) debug_map[i][j] = 1;
    for (i = rect->lx, j = rect->hy; i <= rect->hx; i++) debug_map[i][j] = 1;
    for (i = rect->lx, j = rect->ly; j <= rect->hy; j++) debug_map[i][j] = 1;
    for (i = rect->hx, j = rect->ly; j <= rect->hy; j++) debug_map[i][j] = 1;
  };
  for (i = 0; i < MAP_W; i++) {
    for (j = 0; j < MAP_H; j++) {
      if (cave->map[i][j].pass == TRUE) {
	debug_map[i][j] = 2;
	if (cave->map[i][j].waynavi == TRUE) debug_map[i][j] = 3;
	if (cave->map[i][j].disturb == TRUE) debug_map[i][j] = 4;
      };
    };
  };
  for (j = 0; j < MAP_H; j++) { 
    for (i = 0; i < MAP_W; i++) { 
      switch (debug_map[i][j]) { 
      case 0:
	/* MAP_WALL */
	g_print(" "); 
	break; 
      case 1:
	/* RECT'S BORDER */
	g_print("."); 
	break; 
      case 2:
	/* MAP_PASS */ 
	g_print("#"); 
	break; 
      case 3:
	/* MAP_PASS AND WAYNAVI(monster's route) */
	g_print("+"); 
	break; 
      case 4:
	/* MAP_PASS AND DISTURB */
	g_print("*");
	break;
      };
    }; 
    g_print("\n"); 
  }; 
}

static struct _rect *cave_rect_add(int lx, int ly, int hx, int hy)
{
  struct _rect *rect;
  rect = my_new(struct _rect, 1);
  rect->lx = lx;
  rect->ly = ly;
  rect->hx = hx;
  rect->hy = hy;
  rect->done_split_v = FALSE;
  rect->done_split_h = FALSE;
  cave->rect_list = g_list_append(cave->rect_list, rect);
  return(rect);
}

static struct _couple *cave_couple_add(int v_or_h, struct _rect *rect0, struct _rect *rect1)
{
  struct _couple *couple;
  couple = my_new(struct _couple, 1);
  couple->v_or_h = v_or_h;
  couple->rect0 = rect0;
  couple->rect1 = rect1;
  cave->couple_list = g_list_append(cave->couple_list, couple);
  return(couple);
}

static struct _room *cave_room_add(int lx, int ly, int hx, int hy, gboolean route)
{
  struct _room *room;
  room = my_new(struct _room, 1);
  room->lx = lx;
  room->ly = ly;
  room->hx = hx;
  room->hy = hy;
  room->route = route;
  room->monsterhouse = FALSE;
  cave->room_list = g_list_append(cave->room_list, room);
  return(room);
}
